package com.houxm.jbapu;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class Jbapu extends Model {
	
	public static Jbapu o = new Jbapu();
	
	public static Jbapu newJbapu() {
		return new Jbapu();
	}
	
	@Override
	public Jbapu set(String column, Object obj) {
		this.data.put(column, obj);
		this.updSet.add(column);
		return this;
	}
	
	@Override
	public Jbapu clear() {
		this.data.clear();
		this.updSet.clear();
		return this;
	}
	
	@Override
	public Jbapu put(Map<String, Object> map) {
		data.putAll(map);
		return this;
	}
	
	public List<Jbapu> find(String sql) {
		return find(sql, new Object[0]);
	}
	
	public List<Jbapu> find(String sql, Object... params) {
		Connection conn = null;
		try {
			conn = this.getConfig().getConn();
			return find(conn, sql, params);
		} catch (Exception e) {
			throw new JbapuRuntimeException(e);
		} finally {
			DBUtils.close(conn);
		}
	}

	private Config getConfig() {
		Config c = new Config();
		Config.init();
		return c;
	}

	public List<Jbapu> find(Connection conn, String sql, Object... params) {
		List<Jbapu> ms = null;
		PreparedStatement p = null;
		ResultSet rs = null;
		try {
			p = conn.prepareStatement(sql);
			for (int i=0; i<params.length; i++) {
				p.setObject(i + 1, params[i]);
			}
			rs = p.executeQuery();
			ms = JbapuUtils.assemble(rs);
			
			return ms;
		} catch (Exception e) {
			throw new JbapuRuntimeException(e);
		} finally {
			DBUtils.close(rs);
			DBUtils.close(p);
		}
	}
	
	public Jbapu findFirst(String sql, Object... paras) {
		List<Jbapu> result = find(sql, paras);
		return result.size() > 0 ? result.get(0) : null;
	}
	
	public Jbapu findFirst(String sql) {
		return findFirst(sql, new Object[0]);
	}
	
	public boolean update(String tableName) {
		Config.init();
		if(this.updSet.isEmpty())
			return false;
		try {
			Table t = Tables.o.getTable(tableName);
			List<String> keys = t.getPrimaryKey();
			for (String pKey : keys) {
				Object id = this.get(pKey);
				if (id == null)
					throw new Exception("You can't update table without Primary Key, " + pKey + " can not be null.");
			}
			
			StringBuilder sql = new StringBuilder();
			sql.append("update `").append(t.getDbName()).append("`.`").append(t.getTableName()).append("` set ");
			List<Object> params = new ArrayList<Object>();
			for (Entry<String, Object> e : this.data.entrySet()) {
				String colName = e.getKey();
				if (updSet.contains(colName) && !isPrimaryKey(colName, keys)) {
					if (params.size() > 0) {
						sql.append(", ");
					}
					sql.append('`').append(colName).append("` = ? ");
					params.add(e.getValue());
				}
			}
			sql.append(" where ");
			for (int i=0; i<keys.size(); i++) {
				if (i > 0) {
					sql.append(" and ");
				}
				sql.append('`').append(keys.get(i)).append("` = ?");
				params.add(this.get(keys.get(i)));
			}
			int result = this.executeUpdate(sql.toString(), params.toArray());
			
			if(result>=0) {
				this.updSet.clear();
				return true;
			}else {
				return false;
			}
		} catch (Exception e) {
			throw new JbapuRuntimeException(e);
		}
	}
	
	private boolean isPrimaryKey(String colName, List<String> pKeys) {
		for (String pKey : pKeys) {
			if (colName.equalsIgnoreCase(pKey)) {
				return true;
			}
		}
		return false;
	}
	
	public boolean save(String tableName) {
		Config.init();
		StringBuilder sql = new StringBuilder();
		List<Object> paras = new ArrayList<Object>();
		
		Table t = Tables.o.getTable(tableName);
		sql.append("insert into `").append(t.getDbName()).append("`.`").append(t.getTableName()).append("`(");
		StringBuilder temp = new StringBuilder(") values(");
		for (Entry<String, Object> e: this.data.entrySet()) {
			String colName = e.getKey();
			if (t.containsColumn(colName)) {
				if (paras.size() > 0) {
					sql.append(", ");
					temp.append(", ");
				}
				sql.append('`').append(colName).append('`');
				temp.append('?');
				paras.add(e.getValue());
			}
		}
		sql.append(temp.toString()).append(')');
		Connection conn = null;
		PreparedStatement pst = null;
		try {
			conn = this.getConfig().getConn();
			pst = conn.prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS);
			for (int i=0;i<paras.size(); i++) {
				pst.setObject(i + 1, paras.get(i));
			}
			int result = pst.executeUpdate();
			this.updSet.clear();
			return result>=1;
		} catch (Exception e) {
			throw new JbapuRuntimeException(e);
		}finally{
			DBUtils.close(pst);
			DBUtils.close(conn);
		}
	}
	
	public boolean delete(String tableName) {
		Config.init();
		Table t = Tables.o.getTable(tableName);
		List<String> list = t.getPrimaryKey();
		Object[] params = new Object[list.size()];
		StringBuilder sql = new StringBuilder(45);
		sql.append("delete from `").append(t.getDbName()).append("`.`").append(t.getTableName()).append("` where ");
		for (int i=0;i<list.size(); i++) {
			if (i > 0) {
				sql.append(" and ");
			}
			sql.append('`').append(list.get(i)).append("` = ?");
			params[i] = this.get(list.get(i));
		}
		return this.executeUpdate(sql.toString(), params)>=1;
	}
	
	public boolean deleteById(Table t, Object... params) {
		Config.init();
		List<String> list = t.getPrimaryKey();
		StringBuilder sql = new StringBuilder(45);
		sql.append("delete from `").append(t.getDbName()).append("`.`").append(t.getTableName()).append("` where ");
		for (int i=0;i<list.size(); i++) {
			if (i > 0) {
				sql.append(" and ");
			}
			sql.append('`').append(list.get(i)).append("` = ?");
		}
		return this.executeUpdate(sql.toString(), params)>=1;
	}
	
	public int executeUpdate(String sql, Object... params) {
		Connection conn = null;
		try {
			conn = this.getConfig().getConn();
			return executeUpdate(conn, sql, params);
		} catch (Exception e) {
			throw new JbapuRuntimeException(e);
		} finally {
			DBUtils.close(conn);
		}
	}

	private int executeUpdate(Connection conn, String sql, Object... params) {
		PreparedStatement p = null;
		try {
			p = conn.prepareStatement(sql);
			for (int i=0;i<params.length; i++) {
				p.setObject(i + 1, params[i]);
			}
			int result = p.executeUpdate();
			return result;
		} catch (Exception e) {
			throw new JbapuRuntimeException(e);
		} finally {
			DBUtils.close(p);
		}
	}
}
